#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Pinball many Balls
# beenden: pkill -9 file.py
# Array kopieren, http://stackoverflow.com/questions/6532881/how-to-make-a-copy-of-a-2d-array-in-python

import sys
import time
import random
import pygame
import numpy as np
import math
from copy import copy, deepcopy

ScreenW=30
ScreenH=20
Zoom=20
BallMax=3 # Anzahl Bälle
RasterColor=50
HindernissColor=100
xPos=ScreenW/2 # Mouse-Cursor
yPos=ScreenH/2
PaddelWinkel=0
PaddelX=20
PaddelY=10
PaddelLaenge=8
PlanMax=100

# ------------------------------------------
def PlanErstellen():
  global BallX, BallY, BallRichtung, Bildschirm, PaddelWinkel, Plan # Zugriff auf Variable erhalten

  bestPlan=0
  bestFehler=10000000
  for id in range(0, PlanMax): # Plan-Nr
    # Werte sichern
    safe1=deepcopy(BallX)
    safe2=deepcopy(BallY)
    safe3=deepcopy(BallRichtung)
    safe4=deepcopy(Bildschirm)
    safe5=deepcopy(PaddelWinkel)

    # Plan erstellen
    Fehlerwert=0
#    print "Simuliere Plan #", id
    for x in range(0, PlanMax): # Zeithorizont
      Plan[x]=random.randint(0,10) # Zufallsaktion
      if Plan[x]>2: # öfter mal gar nichts tun
        Plan[x]=0
      #
      # Änderungen vornehmen
      #
      WinkelAlt=PaddelWinkel
      if Plan[x]==1:
        PaddelWinkel-=22.5
      if Plan[x]==2:
        PaddelWinkel+=22.5
      # alten Paddel löschen
      for i in range(0, PaddelLaenge):
        x=int(round(PaddelX+i*math.sin(WinkelAlt*math.pi/180)))
        y=int(round(PaddelY-i*math.cos(WinkelAlt*math.pi/180)))
        Bildschirm[x,y]=0
      # neuen Paddel einzeichnen
      for i in range(0, PaddelLaenge):
        x=int(round(PaddelX+i*math.sin(PaddelWinkel*math.pi/180)))
        y=int(round(PaddelY-i*math.cos(PaddelWinkel*math.pi/180)))
        Bildschirm[x,y]=1
      # Bälle bewegen
      for i in range(0, BallMax):
        Abstand=1
        x=int(round(BallX[i]+Abstand*math.sin(BallRichtung[i]*math.pi/180)))
        y=int(round(BallY[i]-Abstand*math.cos(BallRichtung[i]*math.pi/180)))
        if(Bildschirm[x,y]==1):
          # Hinderniss erreicht, Einschlagwinkel ermitteln
          Einschlagwinkel=0
          if(Bildschirm[x+1,y]==1):
            Einschlagwinkel=90
          if(Bildschirm[x,y+1]==1):
            Einschlagwinkel=180
          BallRichtung[i]=2*Einschlagwinkel-BallRichtung[i]
        else:
          BallX[i]=x
          BallY[i]=y
#        Fehler auf Gesamtstrecke aufaddieren
#        Fehlerwert=Fehlerwert+pow(xPos-BallX[i], 2)+pow((yPos-BallY[i]), 2)
#        Fehlerwert=math.sqrt(Fehlerwert) # quadratischer Fehler

    # Fehler am Ende der Trajektorie
    for i in range(0, BallMax):
      Fehlerwert+=pow(xPos-BallX[i], 2)+pow((yPos-BallY[i]), 2)
      
    # Fehlerwert ausgeben
#    print "Fehlerwert ", Fehlerwert
    if Fehlerwert<bestFehler:
      bestPlan=id
      bestFehler=Fehlerwert
      Plansafe=deepcopy(Plan)

    # Werte zurückschreiben
    BallX=deepcopy(safe1)
    BallY=deepcopy(safe2)
    BallRichtung=deepcopy(safe3)
    Bildschirm=deepcopy(safe4)
    PaddelWinkel=deepcopy(safe5)

  # Auswertung
  print "Bester Plan #",bestPlan, "mit Fehler ",bestFehler
  print Plansafe
  Plan=deepcopy(Plansafe)

# ------------------------------------------
def Ausgabe():
  window.fill((0, 0, 0)) # Bildschirm loeschen

  # Raster zeichnen
  for x in range(0, ScreenW):
    pygame.draw.line(window, (RasterColor,RasterColor,RasterColor), (x*Zoom, 0), (x*Zoom, ScreenW*Zoom))
  for y in range(0, ScreenH):
    pygame.draw.line(window, (RasterColor,RasterColor,RasterColor), (0, y*Zoom), (ScreenW*Zoom, y*Zoom))

  # Bildschirm ausgeben
  for x in range(0, ScreenW):
    for y in range(0, ScreenH):
      if Bildschirm[x,y] == 1: # Hinderniss
        pygame.draw.rect(window, (HindernissColor,HindernissColor,HindernissColor), (x*Zoom,y*Zoom,1*Zoom,1*Zoom))

  # Framerate
  myfont = pygame.font.SysFont("freesans", Zoom)
  label = myfont.render(str(clock), 1, (255,255,255))
  window.blit(label, (300,0))

  # MouseCursor Home einzeichnen
  x=xPos
  y=yPos
  pygame.draw.rect(window, (HindernissColor,2*HindernissColor,HindernissColor), (x*Zoom,y*Zoom,1*Zoom,1*Zoom),2)

  # Bälle ausgeben
  for i in range(0, BallMax):
    pygame.draw.circle(window, (HindernissColor,2*HindernissColor,HindernissColor), (BallX[i]*Zoom+Zoom/2,BallY[i]*Zoom+Zoom/2),10)
  
  pygame.display.flip()

# ------------------------------------------
def Update():
  global PaddelWinkel # Zugriff auf Variable erhalten

  WinkelAlt=PaddelWinkel
  if Plan[counter]==1:
    PaddelWinkel-=22.5
  if Plan[counter]==2:
    PaddelWinkel+=22.5

  # alten Paddel löschen
  for i in range(0, PaddelLaenge):
    x=int(round(PaddelX+i*math.sin(WinkelAlt*math.pi/180)))
    y=int(round(PaddelY-i*math.cos(WinkelAlt*math.pi/180)))
    Bildschirm[x,y]=0

  # neuen Paddel einzeichnen
  for i in range(0, PaddelLaenge):
    x=int(round(PaddelX+i*math.sin(PaddelWinkel*math.pi/180)))
    y=int(round(PaddelY-i*math.cos(PaddelWinkel*math.pi/180)))
    Bildschirm[x,y]=1

  # Bälle bewegen
  for i in range(0, BallMax):
    Abstand=1
    x=int(round(BallX[i]+Abstand*math.sin(BallRichtung[i]*math.pi/180)))
    y=int(round(BallY[i]-Abstand*math.cos(BallRichtung[i]*math.pi/180)))
    if(Bildschirm[x,y]==1):
      # Hinderniss erreicht, Einschlagwinkel ermitteln
      Einschlagwinkel=0
      if(Bildschirm[x+1,y]==1):
        Einschlagwinkel=90
      if(Bildschirm[x,y+1]==1):
        Einschlagwinkel=180
      BallRichtung[i]=2*Einschlagwinkel-BallRichtung[i]
    else:
      BallX[i]=x
      BallY[i]=y



# ------------------------------------------
# M A I N
# ------------------------------------------
# Array 
Bildschirm=np.zeros((ScreenW,ScreenH),dtype=int) # initialisieren mit 0
Plan=np.zeros((PlanMax),dtype=int) # initialisieren mit 0
BallX=np.zeros((BallMax),dtype=int) # initialisieren mit 0
BallY=np.zeros((BallMax),dtype=int) # initialisieren mit 0
BallRichtung=np.zeros((BallMax),dtype=int) # initialisieren mit 0

# Ball mit zufälliger Startposition
for i in range(0, BallMax):
  BallX[i]=random.randint(2,ScreenW-3)
  BallY[i]=random.randint(2,ScreenH-3)
  if random.randint(0,1)==0:
    BallRichtung[i]=45
  else:
    BallRichtung[i]=-45



# Hindernisse festlegen

# Hinderniss am Boden
for x in range(1, ScreenW-1):
  Bildschirm[x,ScreenH-2]=1
# Hinderniss Decke
for x in range(1, ScreenW-1):
  Bildschirm[x,1]=1
# Hinderniss linke Wand
for x in range(1, ScreenH-1):
  Bildschirm[1,x]=1
# Hinderniss rechte Wand
for x in range(1, ScreenH-1):
  Bildschirm[ScreenW-2,x]=1

#create the screen
pygame.init()
window = pygame.display.set_mode((ScreenW*Zoom,ScreenH*Zoom))

#    time.sleep(3.00) # warten

#input handling
counter=0
clock = pygame.time.Clock()
while True: 
  clock.tick(50) # mit 50 fps arbeiten
  counter+=1
  if counter%PlanMax==0:
    PlanErstellen()
    counter=0

  Update()
  Ausgabe()
  for event in pygame.event.get(): 
    if event.type == pygame.QUIT: 
      sys.exit(0) 
    elif event.type == pygame.MOUSEMOTION:
       x, y = event.pos
       xPos=x/Zoom; yPos=y/Zoom;
#       print "mouse at (%d, %d)" % (xPos,yPos)



